﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using ViewMaker.Core.Utils;
using System.ComponentModel;
using System.Windows.Input;
using ViewMaker.Core.Controls;
using System.Diagnostics;
using ViewMaker.Core.Views;
using System.IO;
using System.Windows;
using System.Windows.Controls;

namespace ViewMaker.Core.ViewModels
{
    [View(typeof(ViewLayoutView), Title="Layout Editor")]
#if !SILVERLIGHT
    [ViewProperty(ViewControl.Properties.WindowWidth, 360)]
    [ViewProperty(ViewControl.Properties.WindowHeight, 600)]
#endif
    public class ViewLayoutViewModel : ViewModelBase, IViewLayoutEditor
    {
        internal class GetElementCommandInfo
        {
            public string Name { get; set; }
            public FrameworkElement Element { get; set; }
        }

        /// <summary>
        /// ViewLayaoutのアイテム構造
        /// </summary>
        public ObservableCollection<ViewLayoutItem> Root { get; set; }

        /// <summary>
        /// ViewLayoutを生成するサービス
        /// </summary>
        protected IViewLayoutGenerator ViewLayoutGenerator
        {
            get { return ServiceLocator.GetService<IViewLayoutGenerator>(); }
        }

        /// <summary>
        /// Xamlを生成するサービス
        /// </summary>
        protected IXamlGenerator XamlGenerator
        {
            get { return ServiceLocator.GetService<IXamlGenerator>(); }
        }

        /// <summary>
        /// ViewLayoutを保存するサービス
        /// </summary>
        protected internal IViewLayoutPersistenceService ViewLayoutPersistence
        {
            get { return ServiceLocator.GetService<IViewLayoutPersistenceService>(); }
        }

        /// <summary>
        /// 現在選択されているアイテム
        /// </summary>
        public ViewLayoutItem CurrentItem
        {
            get { return _currentItem; }
            set
            {
                //if (_currentItem == value) return;
                if (_currentItem != null) _currentItem.PropertyChanged -= new PropertyChangedEventHandler(_currentItem_PropertyChanged);
                _currentItem = value;
                OnPropertyChanged("CurrentItem");
                if (_currentItem != null) _currentItem.PropertyChanged += new PropertyChangedEventHandler(_currentItem_PropertyChanged);
                OnPropertyChanged("ItemContent");
                OnPropertyChanged("ControlContent");
            }
        }

        private ViewLayoutItem _currentItem;
        void _currentItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Control")
            {
                OnPropertyChanged("ControlContent");
            }
        }


        /// <summary>
        /// 編集中のViewModel型
        /// </summary>
        public Type ViewModelType {get; private set;}

        /// <summary>
        /// 編集中のXAMLコード
        /// </summary>
        public string XamlCode
        {
            get { return _xamlString; }
            private set { _xamlString = value; OnPropertyChanged("XamlCode"); }
        }
        private string _xamlString;

        /// <summary>
        /// 選択アイテムのコンテンツ
        /// </summary>
        public object ItemContent
        {
            get
            {
                try
                {
                    return CurrentItem == null ? null : ViewUtil.BuildContent(CurrentItem,false);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.ToString());
                    return ex.Message;
                }
            }
        }

        //タイプ別のコンテンツ
        public object ControlContent
        {
            get
            {
                try
                {
                    return CurrentItem == null ? null : ViewUtil.BuildContent(CurrentItem.Element,false);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.ToString());
                    return ex.Message;
                }
            }
        }

        /// <summary>
        /// 処理情報
        /// </summary>
        public string InfoText
        {
            get { return _infoText; }
            set { _infoText = value; OnPropertyChanged("InfoText"); }
        }
        private string _infoText;

        public bool Closing { get; set; }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="targetLayout"></param>
        /// <param name="targetType"></param>
        public ViewLayoutViewModel(ViewLayoutItem targetLayout, Type targetType)
        {
            this.ViewModelType = targetType;
            this.Root = new ObservableCollection<ViewLayoutItem>();
            targetLayout.Owner = this;
            this.Root.Add(targetLayout);
            GenerateXamlCode();
            ResetLayoutData();
        }

        private byte[] orignailLayoutData;

        internal void ResetLayoutData()
        {
            orignailLayoutData = SerializeLayoutData();
        }

        private byte[] SerializeLayoutData()
        {
            using (var st = new MemoryStream())
            {
                this.ViewLayoutPersistence.Serialize(this.Root[0], st);
                return st.ToArray();
            }
        }

        public void Activate()
        {
            ExecuteViewCommand("Activate", null);
        }

        public void Hide()
        {
            if (!SerializeLayoutData().SequenceEqual(orignailLayoutData))
            {
                if (this.ShowOKCancel(string.Format("Do you want to save [{0}] layout?", this.Root[0].Caption)))
                {
                    this.SaveLayoutAction();
                }
            }
            ExecuteViewCommand("Hide", null);
        }

        public void Close()
        {
            Closing = true;
            ExecuteViewCommand(ViewCommands.Close, null);
        }
        public bool SelectByName(string name)
        {
            if (Root.Count == 0) return false;
            var source = Root[0].FindChild(name);
            if (source != null)
            {
                this.CurrentItem = source;
                return true;
            }
            return false;
        }

        public void GenerateXamlCode()
        {
            XamlCode = (Root == null && Root.Count > 0) ? null : XamlGenerator.Generate(Root[0]);
        }

        #region Action

        protected internal override void OnCommandExecuting(ICommand command, object parameter)
        {
            InfoText = "";
            base.OnCommandExecuting(command, parameter);
        }

        /// <summary>
        /// 表示の更新
        /// </summary>
        public ICommand RefreshCommad
        {
            get { return CreateCommand(GenerateXamlCode); }
        }

        /// <summary>
        /// アイテムの生成
        /// </summary>
        public ICommand GenerateItemCommand
        {
            get { return CreateCommand((x) => GenerateItemAction(x), (x) => CurrentItem != null && CurrentItem.Element.CanAddChild); }
        }

        private void GenerateItemAction(object arg)
        {
            string item = (string)arg;
            if (CurrentItem == null) return;
            if (!CurrentItem.Element.CanAddChild) return;
            var prop = CurrentItem.GetBindingType().GetProperty(item);
            if (prop == null) return;
            ViewLayoutItem newItem = ViewLayoutGenerator.GenerateChild(CurrentItem, prop);
            if (newItem == null) return;
            CurrentItem.AddChild(newItem);
            GenerateXamlCode();
        }

        /// <summary>
        /// コントロールの追加
        /// </summary>
        public ICommand AddControlCommand
        {
            get { return CreateCommand(AddControlAction); }
        }

        private void AddControlAction(object arg)
        {
            ViewControlType type = (ViewControlType)arg;
            var newItem = new ViewLayoutItem(CurrentItem, "", type, null, false, "");
            if (CurrentItem.Element.IsPanel || CurrentItem.ControlType == ViewControlType.DataGrid)
            {
                CurrentItem.AddFirst(newItem);
            }
            else
            {
                CurrentItem.AddAfter(newItem);
            }
            GenerateXamlCode();
            this.CurrentItem = newItem;
        }

        /// <summary>
        /// コントロールの挿入
        /// </summary>
        public ICommand InsertControlCommand
        {
            get { return CreateCommand(InsertControlAction); }
        }

        private void InsertControlAction(object arg)
        {
            if (arg == null) arg = ViewControlType.StackPanel;
            ViewControlType type = (ViewControlType)arg;

            ViewLayoutItem newItem = new ViewLayoutItem(null, "", type, null, false, "");
            if (CurrentItem.IsRootElement)
            {
                var work = CurrentItem;
                var owner = work.Owner;
                this.Root.Add(newItem);
                newItem.Rename(newItem.Name);   //重複しない名前に変更
                newItem.Caption = newItem.Name;
                this.Root.Remove(work);
                newItem.AddChild(work);
                newItem.Owner = owner;
                newItem.Element.WindowSettings = work.Element.WindowSettings;
                work.Owner = null;
                work.Element.WindowSettings = null;
            }
            else
            {
                var work = CurrentItem;
                var parent = work.Parent;
                parent.AddChild(newItem);
                newItem.Caption = newItem.Name;
                parent.Children.Move(parent.Children.IndexOf(newItem), parent.Children.IndexOf(work));
                work.Remove();
                newItem.Children.Add(work);
            }
            this.CurrentItem = newItem;
        }

        /// <summary>
        /// コントロールの変更
        /// </summary>
        public ICommand ModifyControlCommand
        {
            get { return CreateCommand((x) => CurrentItem.SetControl((ViewControlType)x)); }
        }

        /// <summary>
        /// Xamlコード表示
        /// </summary>
        public ICommand ShowXamlCommand
        {
            get { return CreateCommand(ShowXamlAction); }
        }

        private void ShowXamlAction()
        {
            var vm = new CodeViewModel();
            vm.Code = XamlCode;
            ViewUtil.ShowDialog(vm);
        }

        /// <summary>
        /// Xamlコード表示
        /// </summary>
        public ICommand ShowCurrentXamlCommand
        {
            get { return CreateCommand(ShowCurrentXamlAction); }
        }

        private void ShowCurrentXamlAction()
        {
            if (CurrentItem == null) return;
            var vm = new CodeViewModel();
            var sb = new StringBuilder();
            foreach (var item in CurrentItem.CreateXaml(XamlNode.LoadXaml("<Temp/>")))
            {
                sb.AppendLine(item.GetXamlCode());
            }
            vm.Code = sb.ToString();
            ViewUtil.ShowDialog(vm);
        }


        /// <summary>
        /// アイテムの削除
        /// </summary>
        public ICommand RemoveItemCommand
        {
            get { return CreateCommand(RemoveItemAction); }
        }

        internal void RemoveItemAction()
        {
            if (CurrentItem.IsRootElement) return;
            var nextPost = CurrentItem.Parent;
            if (CurrentItem.Parent.Children.Count != 1)
            {
                for (int idx = 0; idx < CurrentItem.Parent.Children.Count; idx++)
                {
                    if (CurrentItem.Parent.Children[idx] == this.CurrentItem)
                    {
                        nextPost = CurrentItem.Parent.Children[idx > 0 ? idx - 1 : 1];
                        break;
                    }
                }
            }
            this.CurrentItem.Remove();
            this.CurrentItem = nextPost;
        }

        /// <summary>
        /// レイアウト情報の確認
        /// </summary>
        public ICommand ShowLayoutViewerCommand
        {
            get { return CreateCommand(ShowLayoutViewerAction); }
        }

        private void ShowLayoutViewerAction()
        {
            ViewUtil.ShowDialog(new ViewLayoutViewerViewModel(this));
        }


        public ICommand SaveLayoutCommand
        {
            get { return CreateCommand(SaveLayoutAction); }
        }
        internal void SaveLayoutAction()
        {
            if (Root.Count == 0) return;
            var svc = ServiceLocator.GetService<IViewLayoutPersistenceService>();
            svc.Save(Root[0], svc.GetDefaultLayoutName(ViewModelType));
            InfoText = "Saved";
            orignailLayoutData = SerializeLayoutData();
        }


        [Browsable(false)]
        public ICommand UpCommand
        {
            get { return CreateCommand((X) => UpAction(), (x) => CurrentItem == null || !CurrentItem.IsRootElement); }
        }

        private void UpAction()
        {
            if (CurrentItem == null) return;
            var item = this.CurrentItem;
            this.CurrentItem.Up();
            this.CurrentItem = item;
        }

        [Browsable(false)]
        public ICommand DownCommand
        {
            get { return CreateCommand((x) => DownAction(), (x) => CurrentItem == null || !CurrentItem.IsRootElement); }
        }
        private void DownAction()
        {
            if (CurrentItem == null) return;
            var item = this.CurrentItem;
            this.CurrentItem.Down();
            this.CurrentItem = item;
        }

        [Browsable(false)]
        public ICommand LeftCommand
        {
            get { return CreateCommand((x) => LeftAction(), (x) => CurrentItem == null || !CurrentItem.IsRootElement); }
        }
        private void LeftAction()
        {
            if (CurrentItem == null) return;
            var item = this.CurrentItem;
            this.CurrentItem.Left();
            this.CurrentItem = item;
        }


        [Browsable(false)]
        public ICommand RightCommand
        {
            get { return CreateCommand((x) => RightAction(), (x) => CurrentItem == null || !CurrentItem.IsRootElement); }
        }
        private void RightAction()
        {
            if (CurrentItem == null) return;
            var item = this.CurrentItem;
            this.CurrentItem.Right();
            this.CurrentItem = item;
        }

        public ICommand CloseCommand { get { return CreateCommand(() => Hide()); } }

        [Browsable(false)]
        public ICommand EditViewControlSettingsCommand
        {
            get { return CreateCommand(()=>ViewUtil.ShowDialog(new DataEditViewModel<ViewMakerSettings>(ViewMakerSettings.Instance))); }
        }


        public ICommand ControlTypeSelectionChangedCommand { get { return CreateCommand(ControlTypeSelectionChanged); } }
        private void ControlTypeSelectionChanged()
        {
            if (CurrentItem.ControlType == ViewControlType.Canvas && CurrentItem.Name != null)
            {
                var fe = GetElement(CurrentItem.Name);
                if (fe != null)
                {
                    CurrentItem.Control.Height = (int)fe.ActualHeight;
                    CurrentItem.Control.Width = (int)fe.ActualWidth;
                }
                int top = 0;
                foreach (var child in CurrentItem.Children)
                {
                    CurrentItem.Control.CanvasTop = top;
                    CurrentItem.Control.CanvasLeft = 0;

                    var childFe = GetElement(CurrentItem.Name);
                    if (childFe != null)
                    {
                        top += (int)childFe.ActualHeight;
                    }
                    else
                    {
                        top += ViewMakerSettings.Instance.CanvasVerticalOffset;
                    }

                }
            }
        }

        private FrameworkElement GetElement(string name)
        {
            GetElementCommandInfo info = new GetElementCommandInfo();
            info.Name = name;
            ExecuteViewCommand("GetElement", info);
            return info.Element;
        }

        #endregion
    }
}
